package com.onpositive.units;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.onpositive.knowledge.model.values.IHasValue;

public class ParsedValueSequenceV2 extends ParsedValue {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1963060761606612962L;
	public static String valueSeparator = "#####";
	public static final String ID = "value sequence v2";

	private final String prefix ;
	private final String postfix ;

	protected final ParsedValue[] valuesArray;
	protected final ParsedTerm[] underlyingTermArray ;

	public ParsedValueSequenceV2(ParsedValue[] valuesArray, String comment, String[] prefixAndPostfix ) {
		super(comment);
		this.valuesArray = valuesArray;
		this.underlyingTermArray = createUnderlyingTermArray() ;
		
		if( prefixAndPostfix == null || prefixAndPostfix.length < 2 ){
			prefix = null ;
			postfix = null ;
		}
		else{
			prefix = prefixAndPostfix[0] ;
			postfix = prefixAndPostfix[1] ;
		}
	}	
	
	@Override
	public <T> Collection<T> getValues(Class<T> vs) {
		HashSet<T>vm=new HashSet<T>();
		for (ParsedValue v: this.valuesArray){
			if (vs.isInstance(v)){
				vm.add(vs.cast(v) );
			}
			vm.addAll(v.getValues(vs));
		}
		return vm;
	}

	public ParsedValueSequenceV2( ParsedValue pv, String comment )
	{
		super( comment ) ;		
		ParsedValueSequenceV2 other = (ParsedValueSequenceV2) pv ;
		this.prefix = other.prefix ;
		this.postfix = other.postfix ;
		
		ArrayList<ParsedValue> valArr = new ArrayList<ParsedValue>() ;
		for( ParsedValue oldPv : other.valuesArray ){
			try {
				ParsedValue newPv = oldPv.getClass().getConstructor( ParsedValue.class, String.class ).newInstance( oldPv, null) ;
				valArr.add(newPv) ;
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			} catch (NoSuchMethodException e) {
				e.printStackTrace();
			} catch (SecurityException e) {
				e.printStackTrace();
			}
		}
		this.valuesArray = valArr.toArray( new ParsedValue[valArr.size()] ) ;
		this.underlyingTermArray = createUnderlyingTermArray() ;
	}
	
	protected ParsedValueSequenceV2( ParsedValueSequenceV2 other, Object[] valArr, String comment )
	{
		super( comment ) ;		
		this.prefix = other.prefix ;
		this.postfix = other.postfix ;
		
		ArrayList<ParsedValue> lst = new ArrayList<ParsedValue>() ;
		for( Object o : valArr ){
			if( o instanceof ParsedValue )
				lst.add((ParsedValue) o) ;
		}			
		
		this.valuesArray = lst.toArray( new ParsedValue[lst.size()]) ;
		this.underlyingTermArray = createUnderlyingTermArray() ;
	}

	public ParsedValueSequenceV2(Element element) {
		super(element);

		String arrSizeStr = element.getAttribute("array-size");
		int size = Integer.parseInt(arrSizeStr);

		ArrayList<ParsedValue> valLst = new ArrayList<ParsedValue>(size);
		for (int i = 0; i < size; i++)
			valLst.add(null);

		NodeList children = element.getChildNodes();
		int l = children.getLength();
		// int prev = 0 ;
		for (int i = 0; i < l; i++) {
			Node n = children.item(i);
			if (n instanceof Element) {
				Element valueElement = (Element) n;
				String indStr = valueElement.getAttribute("local-index");
				int ind = Integer.parseInt(indStr);
				ParsedValue value = XMLValueParser
						.produceParsedValue(valueElement);
				valLst.set(ind, value);
				// prev = ind ;
			}
		}
		ParsedValue[] array = valLst.toArray(new ParsedValue[valLst.size()]);
		valuesArray = array;
		this.underlyingTermArray = createUnderlyingTermArray() ;
		
		String prefStr = element.getAttribute("prefix").trim();
		this.prefix = prefStr.length() != 0 ? prefStr : null ;
		
		String postfStr = element.getAttribute("postfix");
		this.postfix = postfStr.length() != 0 ? postfStr : null;
	}

	@Override
	public void writeValue(Element element) {

		super.writeValuePartial(element);
		element.setAttribute("array-size", "" + valuesArray.length);
		int ind = 0;
		for (ParsedValue val : valuesArray) {
			Element newElement = element.getOwnerDocument()
					.createElement("val");
			newElement.setAttribute("local-index", "" + (ind++));
			element.appendChild(newElement);
			val.writeValue(newElement);
		}

		if (prefix != null)
			element.setAttribute("prefix", prefix);

		if (postfix != null)
			element.setAttribute("postfix", postfix);
	}

	@Override
	public final String getContextFragmentString() {
		StringBuilder bld = new StringBuilder();
		if (prefix != null)
			bld.append(prefix);
		
		bld.append( getContentString() ) ;
		
		if (postfix != null)
			bld.append(postfix);

		final String result = bld.toString() ;
		return result;
	}
	
	@Override
	public final String getContentString()
	{
		StringBuilder bld = new StringBuilder();

		boolean gotValues = false ;
		for (ParsedValue pv : valuesArray) {
			bld.append( pv.getContextFragmentString() );
			bld.append(" ") ;
			gotValues = true ;
		}
		if( gotValues )
			bld.delete( bld.length()-1, bld.length() ) ;
		
		final String result = bld.toString() ;
		return result;
	}
	
	private ParsedTerm[] createUnderlyingTermArray() {
		
		ArrayList<ParsedTerm> lst = extractTerms( this ) ;		
		return lst.toArray( new ParsedTerm[ lst.size() ] );
	}

	private static ArrayList<ParsedTerm> extractTerms( ParsedValueSequenceV2 parsedValueSequenceV2 ) {
		
		ArrayList<ParsedTerm> lst = new ArrayList<ParsedTerm>() ;
		ParsedValue[] valArray = parsedValueSequenceV2.getValuesArray() ;
		for( ParsedValue pv : valArray ){
			if( pv instanceof ParsedTerm )
				lst.add((ParsedTerm) pv) ;
			else if( pv instanceof ParsedValueSequenceV2 ){
				lst.addAll( extractTerms((ParsedValueSequenceV2) pv) ) ;
			}
		}
		return lst ;		
	}

	@Override
	public Object value() {
		return valuesArray;
	}

	@Override
	public void setValue(Object obj) {
	}

	@Override
	public Object describer() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getId() {
		return ID;
	}

	@Override
	public String getValueAsString() {

		StringBuilder builder = new StringBuilder();

		if (valuesArray.length == 0)
			return "";

		for (ParsedValue pv : valuesArray) {
			builder.append(pv.getValueAsString());
			// final String comment = pv.getComment();
			//
			// if( comment != null && comment.length() != 0 ){
			// builder.append( commentSeparator ) ;
			// builder.append( comment ) ;
			// }
			//
			builder.append(valueSeparator);
		}
		builder.delete(builder.length() - valueSeparator.length(),
				builder.length());
		return builder.toString();
	}

	public String getPrefix() {
		return prefix;
	}

	public String getPostfix() {
		return postfix;
	}

	public ParsedValue[] getValuesArray() {
		return valuesArray;
	}

	public ParsedTerm[] getUnderlyingTermArray() {
		return underlyingTermArray;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		ParsedValueSequenceV2 other = (ParsedValueSequenceV2) obj;
		if (!Arrays.equals(valuesArray, other.valuesArray))
			return false;
		return true;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + Arrays.hashCode(valuesArray);
		return result;
	}

	@Override
	public IHasValue clone(Object value, String comment) {
		if( !(value instanceof ParsedValue[]) )
			return null;
		
		if( comment == null )
			comment = this.getComment() ;
		
		ParsedValue[] valArr = (ParsedValue[]) value ;
		return new ParsedValueSequenceV2( this, valArr, comment ) ;		
	}
}